#!/usr/bin/env python
#
# Generic reader for flat data
#
# Author:   Mike Murphy (mamurph@cs.clemson.edu)
# Revision: 23 September 2009
#
#   Copyright 2009 Clemson University
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#
# This material is based upon work supported under a National Science
# Foundation Graduate Research Fellowship. Any opinions, findings,
# conclusions or recommendations expressed in this publication are those of
# the author(s) and do not necessarily reflect the views of the National
# Science Foundation.

'''
This module provides mechanisms for parsing a whitespace-delimited,
line-oriented text file. Lines may be filtered using a "linefilter" mechanism,
which uses a tuple of types to identify valid lines in the file. These tuples
have forms such as::

    (int, int)          Matches lines containing two integers
    (str)               Matches lines containing a single string

Fields in the input file must be separated by whitespace. Quotes strings
containing whitespace are NOT supported. All whitespace will be stripped from
all tokens.

Note that the "str" type will match anything, including ints and floats.
'''

from data_input import Data


class FlatData(Data):
   '''
   Mechanism for handling whitespace-delimited text input files. To be useful,
   subclasses should override the record_handler() method of this class,
   which will receive a list of columns found in a line that has passed
   the linefilter.
   '''
   def __init__(self, filename, fh, linefilter):
      '''
      @param filename: Name of the open file
      @type filename: str
      @param fh: Open file handle (r mode)
      @param linefilter: Tuple of types to match for each valid line in
                         the file
      '''
      Data.__init__(self, filename, fh)
      self.linefilter = linefilter
   def line_parser(self, line, eof, filename, line_number):
      '''
      Line parser for flat data. Each line that satisfies the type constraints
      given in the linefilter is converted to a list of columns, where each
      column has been cast to the requested type.

      @param line: Raw input line
      @type line: str
      @param eof: End of file flag
      @type eof: bool
      @param filename: Name of the input file
      @type filename: str
      @param line_number: Current line number of input file
      @type line_number: int

      @return: a list of columns if the line passes the typefilter, or None
      if the line is invalid. Subclasses should override the record_handler()
      method, which will receive the list of columns for each valid line.
      '''
      cur_row = []
      retval = None
      if not eof:
         rawparts = line.split()
         parts = [ part.strip() for part in rawparts if len(part) > 0 ]
         line_ok = False
         if len(parts) == len(self.linefilter):
            idx = 0
            for part in parts:
               try:
                  item = self.linefilter[idx](part)
                  cur_row.append(item)
                  line_ok = True
               except Exception:
                  line_ok = False
                  break
               idx += 1
            #
         if line_ok:
            retval = cur_row
         #
      #
      return retval
#
